昨天我們學會了:
__init__
:建構子,用來初始化資料self
:物件自己的指標,幫我們區分不同物件那麼今天,我們要讓物件變得「更聰明、更有互動性」——
不只會自顧自跑程式,而是能互相連結、交換資料,
還要學會超實用的觀念:繼承(Inheritance)!
想像你在經營一間補習班:
這時候就出現「物件互動」:
不同物件之間彼此連結、共享資料。
最關鍵的觀念是:「資料同步!」
如果用傳統函式寫法,要追蹤「誰選了哪門課」超容易出錯。
但透過物件導向思維,我們可以讓:
只要透過互相呼叫方法,就能讓資料自動同步!
範例:學生選課系統
class Course:
def __init__(self, name):
self.name = name
self.students = [] # 課程裡的學生清單
def add_student(self, student):
self.students.append(student)
print(f"{student.name} 選了課程 {self.name}")
def show_students(self):
print(f"課程 {self.name} 的學生有:")
for s in self.students:
print(f"- {s.name}")
class Student:
def __init__(self, name, grade):
self.name = name
self.grade = grade
self.courses = [] # 學生選的課程清單
def enroll(self, course):
self.courses.append(course)
course.add_student(self)
print(f"{self.name} 已成功加入 {course.name}")
def show_courses(self):
print(f"{self.name} 選的課程有:")
for c in self.courses:
print(f"- {c.name}")
# 建立學生
alice = Student("Alice", 5)
bob = Student("Bob", 6)
# 建立課程
math = Course("數學")
english = Course("英文")
# 學生選課
alice.enroll(math)
alice.enroll(english)
bob.enroll(math)
輸出:
說明:
alice.enroll(math)
math.add_student(alice)
→ 數學課也知道 Alice 選了它只更新一邊資料
→ 導致學生記得有選課,課程卻沒記錄他。
忘記呼叫方法
→ 把 add_student
寫好卻沒執行,畫面沒任何變化。
查詢資料:
alice.show_courses()
bob.show_courses()
math.show_students()
english.show_students()
輸出會完整顯示:
輸出:
想像你有三個角色:
他們都有「名字」和「年齡」,但各自又有不同功能(教課、讀書、打卡)。
若你三個類別都寫一遍 name
、age
、say_hello()
,會重複超多程式碼!
如果還是有點不太清楚的話,這邊換個方式解說:
Person
是「人」的基本規格(name、age),學生(Student)與老師(Teacher)都是「人」,但會有額外屬性(學生有年級、老師有科目)。Person
,學生與老師直接「繼承」,就不用在每個類別裡重複寫 name
的處理。好處:
範例:Person → Student & Teacher
class Person:
def __init__(self, name):
self.name = name
def show_name(self):
print(f"姓名:{self.name}")
class Student(Person):
def __init__(self, name, grade):
super().__init__(name) # 呼叫父類別初始化 name
self.grade = grade
def show_info(self):
print(f"學生: {self.name}, 年級: {self.grade}")
class Teacher(Person):
def __init__(self, name, subject):
super().__init__(name)
self.subject = subject
def show_info(self):
print(f"老師: {self.name}, 教學科目: {self.subject}")
# 建立物件
s1 = Student("Alice", 5)
t1 = Teacher("Mr. Lee", "數學")
s1.show_name() # 父類別方法
s1.show_info() # 子類別方法
t1.show_name()
t1.show_info()
說明:
Person
:父類別(共用屬性 name
與方法 show_name
)。Student(Person)
:表示 Student 繼承 Person。
super().__init__(name)
:呼叫父類別的初始化,把 name
初始化好(否則 self.name
會不存在)。Student.total_students
:類別變數(放在 class 層級),用來統計總學生數(注意:不是每個學生都有獨立 copy)。Teacher(Person)
:同理,繼承 Person
,並新增 subject
。輸出:
忘記 super().__init__(name, age)
→ 子類別沒呼叫父類別建構子,就會缺屬性!
把 super()
寫成 self.super()
(常見錯誤)
我們再多看一種範例:
class Vehicle: # 父類別(通用的交通工具)
def __init__(self, brand):
self.brand = brand
def move(self):
print(f"{self.brand} 正在移動中")
class Car(Vehicle): # 子類別(汽車)
def honk(self):
print(f"{self.brand} 按喇叭:嗶嗶!")
class Bike(Vehicle): # 子類別(腳踏車)
def ring_bell(self):
print(f"{self.brand} 按鈴:叮叮~")
Vehicle
是「父類別」,裡面定義了交通工具的基本功能。Car(Vehicle)
表示 Car 繼承 Vehicle 的所有特性。
__init__
和 move()
。honk()
、Bike 有 ring_bell()
。car = Car("Toyota")
bike = Bike("Giant")
car.move() # 從父類別繼承而來
car.honk() # 子類別自己定義
bike.move() # 從父類別繼承
bike.ring_bell() #子類別自己定義
輸出:
說來說去,看到這邊的你應該會有一個疑惑就是:
為什麼要繼承?
想像你要開一間補習班,會有:
他們其實都有一些共通特性,像是:
但他們又有一些「特別的功能」:
如果你為每個角色都從頭寫一個 class,
那這些「共通的部分」就得重複三次,非常浪費。
所以我們可以建立一個「父類別」── Person,
然後讓 Teacher、Student、Staff 全部繼承自 Person!
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def say_hello(self):
print(f"嗨,我是 {self.name},今年 {self.age} 歲!")
# 子類別繼承 Person
class Teacher(Person):
def teach(self):
print(f"{self.name} 正在上課中~")
class Student(Person):
def study(self):
print(f"{self.name} 正在努力讀書中 ")
# 測試
teacher = Teacher("王老師", 35)
student = Student("小明", 15)
teacher.say_hello()
teacher.teach()
student.say_hello()
student.study()
Teacher(Person)
表示 Teacher 是繼承自 Person。__init__
和 say_hello()
。輸出:
有時候子類別會想要「稍微改一下」父類別的功能。
這時候可以用「覆寫(override)」來取代父類別的方法。(延續上題)
class Student(Person):
def say_hello(self):
print(f"哈囉~我是學生 {self.name},我今年 {self.age} 歲喔!")
student = Student("小美", 14)
student.say_hello() # 使用子類別版本,而非父類別的
「覆寫」就像是在繼承的基礎上「客製化」行為。
你還是「繼承」了那個功能的名字,只是裡面的內容換成你自己的版本。
輸出:
有時候你想「在保留父類別原有功能的同時,再加一些東西」。這時候就要用到 super()
!
class Teacher(Person):
def say_hello(self):
super().say_hello() # 先呼叫父類別的方法
print("我是一位補習班老師")
teacher = Teacher("陳老師", 40)
teacher.say_hello()
輸出:
# ====== 類別定義區 ======
class MenuItem:
def __init__(self, name, price):
self.name = name
self.price = price
def info(self):
print(f"{self.name} - ${self.price}")
class Drink(MenuItem):
def __init__(self, name, price, size):
super().__init__(name, price)
self.size = size
def info(self):
print(f" {self.name} ({self.size}) - ${self.price}")
class MainDish(MenuItem):
def __init__(self, name, price, spicy=False):
super().__init__(name, price)
self.spicy = spicy
def info(self):
spicy_mark = "🌶️" if self.spicy else ""
print(f" {self.name}{spicy_mark} - ${self.price}")
class Dessert(MenuItem):
def __init__(self, name, price, sweetness):
super().__init__(name, price)
self.sweetness = sweetness
def info(self):
print(f"{self.name}(甜度:{self.sweetness}/5) - ${self.price}")
# ====== 建立菜單 ======
menu = [
Drink("紅茶", 30, "M"),
Drink("拿鐵", 60, "L"),
MainDish("咖哩飯", 120, True),
MainDish("燉飯", 150, False),
Dessert("布朗尼", 80, 4),
Dessert("抹茶蛋糕", 90, 3)
]
# ====== 主程式互動區 ======
print("=== 🍽️ 歡迎光臨IT人餐廳!===")
order_list = []
while True:
print("\n--- 今日菜單 ---")
for i, item in enumerate(menu, start=1):
print(f"{i}. ", end="")
item.info()
choice = input("\n請輸入想點的餐點編號(輸入 q 結束點餐):")
if choice.lower() == "q":
break
if not choice.isdigit() or not (1 <= int(choice) <= len(menu)):
print("⚠️ 無效輸入,請重新輸入餐點編號。")
continue
item = menu[int(choice) - 1]
order_list.append(item)
print(f"✅ 已將「{item.name}」加入餐點。")
# 結帳
print("\n===🧾結帳明細 ===")
total = 0
for o in order_list:
o.info()
total += o.price
print(f"\n💵 總金額:${total}")
print("感謝您的光臨")
我們先定義一個父類別 MenuItem
,代表菜單上的每一項。
每個項目都有 名稱 和 價格。
接著,我們創建三個子類別:
Drink
(飲料)→ 有「大小杯」資訊MainDish
(主餐)→ 可以標註是否「辣」Dessert
(甜點)→ 有「甜度」資訊輸出:
完整輸出:
再次提醒新手常見錯誤:
self.super().__init__(name, price)
# 這樣寫是錯的!
因為 super()
是一個內建函式,不需要加 self.
。
正確寫法:
super().__init__(name, price)
#這樣才是正確的
老實說,當初我剛學「繼承」時超級懷疑人生。
心想:「我就直接寫三個類別不行嗎?幹嘛搞這麼複雜?」
但當你開始實作像這種「餐廳系統」時,就會突然懂了:
原來繼承的重點不是省幾行程式碼,
而是讓整個系統有「延展性」與「一致性」。
今天多加一個類別,比如「湯品」,
我根本不用改前面的架構,只要繼承 MenuItem
,加個新屬性就搞定。
這樣也很省時對吧!!
今天辛苦啦!希望讀者有跟上進度~這邊確實會有點抽象!!
不過多看幾次、多試幾次,就會進步很多的!!
一起加油~迎接最後一週!!時間過超快的!
那麼我們就明天見囉!!